package com.apobates.jforum.grief.aurl.executor;

import com.apobates.jforum.grief.aurl.core.AUrlMeta;
import com.apobates.jforum.grief.aurl.core.AUrlMetaBuilder;
import com.apobates.jforum.grief.aurl.spring.StringUtils;
import com.apobates.jforum.grief.aurl.utils.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 路径修正执行器
 * 使用相对路径对当前信息进行修正
 */
public class ResolveExecutor extends AUrlMetaExecutor<Pair<String,String>>{
    private final Set<Pair<String,String>> actionRecords;
    // 替换相对路径符号
    private static final Pattern RELATIVE_SYMBOL = Pattern.compile("[.]+/");
    private static final Logger logger = LoggerFactory.getLogger(ResolveExecutor.class);

    public ResolveExecutor(AUrlMeta rawMeta) {
        super(rawMeta);
        this.actionRecords = new HashSet<>();
    }

    @Override
    public Set<Pair<String,String>> getActionRecords() {
        return actionRecords;
    }

    public ResolveExecutor relative(String relativePath){
        if(!StringUtils.hasLength(relativePath)){
            throw new IllegalArgumentException("参数不可用");
        }
        if(!relativePath.startsWith("../") && !relativePath.startsWith("./")){
            throw new IllegalArgumentException("参数不满足相对路径格式");
        }
        final Map<String,String> relaMeta = splitPathSegment(relativePath);
        // 从relativePath提取:path,query,fegment
        String _pathVal=Optional.ofNullable(relaMeta.get("path")).orElse(PATH_DELIMITER_STR);
        // 清空相对路径符号并无以:/开头的
        String repath = StringUtils.regReplaceBlank(_pathVal, RELATIVE_SYMBOL);
        // 计算结果的最终path
        // 有几个../?
        int psst = new StringTokenizer(_pathVal, "..").countTokens();
        // 不能以:/开头
        String finalPath = getRawMeta().getPath();
        if(psst > 0){ // 可以截断
            // 计算前半部分(丢弃多少:/段)
            String[] pathSegment = finalPath.split(PATH_DELIMITER_STR);
            finalPath = (pathSegment.length>psst && pathSegment.length>0)?
                    String.join(PATH_DELIMITER_STR, Arrays.copyOfRange(pathSegment, 0, pathSegment.length - psst)).concat(PATH_DELIMITER_STR).concat(repath):
                    repath;
        }else{
            // finalPath拼接relativePath
            finalPath = StringUtils.hasLength(finalPath)?finalPath.concat(PATH_DELIMITER_STR).concat(repath):finalPath;
        }
        logger.debug("[RES]path:"+finalPath+",query:"+relaMeta.get("query")+",fragment:"+relaMeta.get("fragment"));
        this.actionRecords.add(Pair.of("path", finalPath));
        if(relaMeta.containsKey("query")) {
            this.actionRecords.add(Pair.of("query", relaMeta.get("query")));
        }
        if(relaMeta.containsKey("fragment")) {
            this.actionRecords.add(Pair.of("fragment", relaMeta.get("fragment")));
        }
        return this;
    }

    @Override
    public AUrlMetaBuilder getBuilder() {
        final Map<String,String> actions = toMap();
        return AUrlMetaBuilder.getInstance()
                .scheme(getRawMeta().getScheme())
                .host(getRawMeta().getHost())
                .userInfo(getRawMeta().getUserInfo())
                .port(getRawMeta().getPort())
                .path(actions.get("path"))
                .query(Optional.ofNullable(actions.get("query")).orElse(getRawMeta().getQuery()))
                .fragment(Optional.ofNullable(actions.get("fragment")).orElse(getRawMeta().getFragment()));
    }
    // 分解出?path,query,fragment
    private static Map<String,String> splitPathSegment(String val){
        //query的标识
        int qIndex = val.indexOf("?");
        //fegment的标识
        int fIndex = val.lastIndexOf("#");
        Map<String,String> data = new HashMap<>();
        if(qIndex == -1 && fIndex == -1){
            data.put("path", val);
            return data;
        }
        if(qIndex != -1){
            data.put("path", val.substring(0, qIndex));
            if(fIndex!=-1){ // 全有了
                data.put("query", val.substring(qIndex+1, fIndex));
                data.put("fragment", val.substring(fIndex+1));
            }else{ // 只有两者
                data.put("query", val.substring(qIndex+1));
            }
        }else{
            if(fIndex!=-1) { // 全有了
                data.put("path", val.substring(0, fIndex));
                data.put("fragment", val.substring(fIndex+1));
            }
        }

        return data;
    }

    private Map<String,String> toMap(){
        return this.actionRecords.stream().collect(Collectors.toMap(pair->pair.left(), pair -> pair.right()));
    }
}
